package com.openroom.ui;

import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;

import javax.imageio.ImageIO;

import org.apache.log4j.Logger;
import org.apache.pivot.serialization.SerializationException;
import org.apache.pivot.util.concurrent.Task;
import org.apache.pivot.util.concurrent.TaskExecutionException;
import org.apache.pivot.util.concurrent.TaskListener;
import org.apache.pivot.wtk.ActivityIndicator;
import org.apache.pivot.wtk.ApplicationContext;
import org.apache.pivot.wtk.ApplicationContext.ScheduledCallback;
import org.apache.pivot.wtk.BoxPane;
import org.apache.pivot.wtk.Component;
import org.apache.pivot.wtk.ComponentMouseButtonListener;
import org.apache.pivot.wtk.ComponentMouseListener;
import org.apache.pivot.wtk.Dimensions;
import org.apache.pivot.wtk.Mouse.Button;
import org.apache.pivot.wtk.media.Image;
import org.apache.pivot.wtk.media.Picture;

import com.jphotomanager.io.ImageLibrary;
import com.openroom.editing.JPEGImageProcessor;
import com.openroom.event.ApplicationEvent;
import com.openroom.event.EventHandler;
import com.openroom.event.EventManager;
import com.openroom.event.EventType;
import com.openroom.model.ImageHolder;
import com.openroom.model.ModelLocator;
import com.openroom.model.ModelLocator.ModeFlag.CropMode;
import com.openroom.ui.component.BorderImage;
import com.openroom.ui.component.CustomOverlayDecorator;

public class PreviewImageView extends BorderImage {

    private static final Logger LOGGER = Logger
            .getLogger(PreviewImageView.class);

    private boolean mouseDown = false;
    private int mx = 0;
    private int my = 0;
    private JPEGImageProcessor ip;

    private ModelLocator model = ModelLocator.getInstance();

    private Rectangle roiRect;

    private Rectangle displayedCropRectangle = null;

    private ActivityIndicator overlay;

    private ScheduledCallback updateCallback;


    public class RotationEventHandler implements EventHandler {
        @Override
        public void handle(ApplicationEvent event) {
            switch (event.getType()) {
            case ROTATE_LEFT:
                markBusy();
                Task<Void> tsk1 = new Task<Void>() {
                    @Override
                    public Void execute() throws TaskExecutionException {
                        BufferedImage rotateLeft = ImageLibrary.getInstance()
                                .rotateLeft();
                        setImage(new Picture(rotateLeft));
                        return null;
                    }
                };

                executeTask(tsk1);

                break;
            case ROTATE_RIGHT:
                markBusy();
                Task<Void> tsk2 = new Task<Void>() {
                    @Override
                    public Void execute() throws TaskExecutionException {
                        BufferedImage rotateLeft = ImageLibrary.getInstance()
                                .rotateRight();
                        setImage(new Picture(rotateLeft));
                        return null;
                    }
                };
                executeTask(tsk2);

                break;
            case FLIP_HORIZONTAL:
                markBusy();
                Task<Void> tsk3 = new Task<Void>() {
                    @Override
                    public Void execute() throws TaskExecutionException {
                        BufferedImage rotateLeft = ImageLibrary.getInstance()
                                .flipHorizontal();
                        setImage(new Picture(rotateLeft));
                        return null;
                    }
                };
                executeTask(tsk3);
                break;
            case FLIP_VERTICAL:
                markBusy();
                Task<Void> tsk4 = new Task<Void>() {
                    @Override
                    public Void execute() throws TaskExecutionException {
                        BufferedImage rotateLeft = ImageLibrary.getInstance()
                                .flipVertical();
                        setImage(new Picture(rotateLeft));
                        return null;
                    }
                };
                executeTask(tsk4);
                break;
            case REVERT_TO_ORIGINAL:
                ImageLibrary.getInstance().revertToOriginal();
                break;
            case CROP_IMAGE:
                if (model.getCurrentMode().cropMode == CropMode.NO_CROP)
                    return;
                if (roiRect.width > 0 && roiRect.height > 0) {
                    markBusy();
                    try {
                        JPEGImageProcessor jpegip = null;
                        final int w = ((Picture) getImage()).getBufferedImage()
                                .getWidth();
                        final int h = ((Picture) getImage()).getBufferedImage()
                                .getHeight();
                        Task<Void> tsk = new Task<Void>() {
                            @Override
                            public Void execute() throws TaskExecutionException {
                                try {
                                    ImageHolder ih = cropFromOriginal(w, h);
                                } catch (IOException e) {
                                    e.printStackTrace();
                                }
                                return null;
                            }

                        };
                        executeTask(tsk);

                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
                ModelLocator.getInstance().getCurrentMode().cropMode = CropMode.NO_CROP;
                break;
            case AUTO_WHITE_BALANCE:
                markBusy();
                Task<Void> tsk = new Task<Void>() {
                    @Override
                    public Void execute() throws TaskExecutionException {
                        ImageHolder ih = ImageLibrary.getInstance().fixWB();
                        setImage(new Picture(ih.getPreviewImage()));
                        ImageLibrary.updateCurrentImage(ModelLocator
                                .getInstance().getCurrentlySelectedImage(),
                                null, ih.getPreviewImage(), ih
                                        .getThumbnailImage());
                        return null;
                    }
                };
                executeTask(tsk);
                break;
            default:
                setImage(new Picture(ImageLibrary.getInstance()
                        .getPreviewImage()));
                break;
            }
        }

        private void executeTask(Task<Void> tsk1) {
            tsk1.execute(new TaskListener<Void>() {
                @Override
                public void taskExecuted(Task<Void> task) {
                    layout();
                    invalidate();
                    doneBusy();
                }

                @Override
                public void executeFailed(Task<Void> task) {
                    doneBusy();
                    LOGGER.error("error", task.getFault());
                }
            });
        }

        private ImageHolder cropFromOriginal(int width, int height)
                throws IOException {
            JPEGImageProcessor ip2 = null;
            BufferedImage originalImage = ImageIO.read(new File(model
                    .getCurrentlySelectedImage().getRawFilePath()));
            float scalex = (float) width / originalImage.getWidth();
            float scaley = (float) height / originalImage.getHeight();
            Rectangle cropRect = ((PreviewImageViewSkin) getSkin())
                    .getCropRectangleOnOriginalImage(roiRect.x, roiRect.y,
                            roiRect.width, roiRect.height, scalex, scaley);
            BufferedImage rotateLeft = ImageLibrary.getInstance()
                    .crop(cropRect);
            setImage(new Picture(rotateLeft));
            return null;
        }
    }

    private final class MouseButtonListener implements
            ComponentMouseButtonListener {

        @Override
        public boolean mouseDown(Component component, Button button, int x,
                int y) {
            if (model.getCurrentMode().cropMode == CropMode.NO_CROP) {
                return false;
            }
            if (!mouseDown) {
                ((PreviewImageView) component).mouseDown = true;
                mx = x;
                my = y;
            }
            return false;
        }

        @Override
        public boolean mouseUp(Component component, Button button, int x, int y) {
            if (model.getCurrentMode().cropMode == CropMode.NO_CROP) {
                return false;
            }
            ((PreviewImageView) component).mouseDown = false;
            int startx = 0;
            int starty = 0;
            int w = 0;
            int h = 0;
            if (x > mx) {
                w = x - mx;
                startx = mx;
            } else {
                w = mx - x;
                startx = x;
            }
            if (y > my) {

                h = y - my;
                starty = my;
            } else {
                h = my - y;
                starty = y;
            }
            try {
                displayedCropRectangle = new Rectangle(startx, starty, w, h);
                roiRect = ((PreviewImageViewSkin) getSkin())
                        .getCropRectangleOnPreviewImage(startx, starty, w, h,
                                component);

                if (roiRect.width > 0 && roiRect.height > 0) {
                    BufferedImage subimage = ((Picture) getImage())
                            .getBufferedImage().getSubimage(roiRect.x,
                                    roiRect.y, roiRect.width, roiRect.height);
                    BufferedImage resizedImage = new BufferedImage(w, h,
                            BufferedImage.TYPE_INT_RGB);
                    Graphics2D g = resizedImage.createGraphics();
                    g.drawImage(subimage, 0, 0, w, h, null);
                    g.dispose();
                    ((PreviewImageViewSkin) getSkin())
                            .paintCropSelectionRectange(
                                    component.getGraphics(), startx, starty,
                                    roiRect.width, roiRect.height, resizedImage);
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
            return false;
        }

        @Override
        public boolean mouseClick(Component component, Button button, int x,
                int y, int count) {
            return false;
        }
    }

    public PreviewImageView() {
        this(null);
        mouseDown = false;
    }

    public PreviewImageView(final Image image) {
        super(image);
        installSkin(PreviewImageView.class);
        getComponentMouseButtonListeners().add(new MouseButtonListener());
        overlay = new ActivityIndicator();
        overlay.setPreferredSize(new Dimensions(50, 50));
        BoxPane boxPane = new BoxPane();
        try {
            boxPane.setStyles("{horizontalAlignment:'center',verticalAlignment:'center'}");
        } catch (SerializationException e1) {
            e1.printStackTrace();
        }
        boxPane.add(overlay);
        final CustomOverlayDecorator decorator = new CustomOverlayDecorator(
                boxPane);
        getDecorators().add(decorator);
        getComponentMouseListeners().add(new ComponentMouseListener() {
            @Override
            public boolean mouseMove(Component component, int x, int y) {
                if (((PreviewImageView) component).mouseDown) {
                    ((PreviewImageViewSkin) getSkin()).paintCropRectange(
                            component.getGraphics(), mx, my, x, y);
                }
                return false;
            }

            @Override
            public void mouseOver(Component component) {

            }

            @Override
            public void mouseOut(Component component) {

            }
        });
        EventManager.getInstance().listenToEventType(
                new RotationEventHandler(), EventType.ROTATE_LEFT,
                EventType.ROTATE_RIGHT, EventType.FLIP_HORIZONTAL,
                EventType.FLIP_VERTICAL, EventType.CROP_IMAGE,
                EventType.REVERT_TO_ORIGINAL, EventType.AUTO_WHITE_BALANCE,
                EventType.RELOAD_IMAGE);
    }

    @Override
    public void setImage(Image image) {
        super.setImage(image);
        setIp(null);
    }

    public void setPreviewImage(Image image) {
        setImage(image);
        setIp(null);
    }

    public void repaintWithDBEnabled() {
        ((PreviewImageViewSkin) getSkin()).layout();
        ((PreviewImageViewSkin) getSkin()).repaintDoubleBuffered(getGraphics());
    }

    public JPEGImageProcessor getIp() {
        return ip;
    }

    public JPEGImageProcessor getOrCreateIp() {
        if (ip == null) {
            try {
                BufferedImage read = ImageIO.read(new File(model
                        .getCurrentlySelectedImage().getPreviewImagePath()));
                setIp(JPEGImageProcessor.createImageProcessor(read));
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return ip;
    }

    public void setIp(JPEGImageProcessor ip) {
        this.ip = ip;
    }

    public void markBusy() {
        overlay.setActive(true);
        updateCallback = ApplicationContext.scheduleRecurringCallback(
                new Runnable() {
                    @Override
                    public void run() {
                        repaint();
                    }
                }, 100);
    }

    public void doneBusy() {
        overlay.setActive(false);
        updateCallback.cancel();
    }

}
